
/*
*Released on 28th June 2008.
*Any violations can be reported at paritosh@wikiocean.net
*What is treated as violations can be found at www.wikiocean.net/ppl1-voilations*
******************************************************************************
* The contents of this file are subject to POOL Public License 1.0 and later.
* POOL Public License is based on Affero GPL v3 and additional conditions.
* ("License"); You may not use this file except in compliance with the License
* You may obtain a copy of the Affero GPL v3 License at http://www.gnu.org/licenses/agpl.html
* You may obtain a copy of the POOL Public License 1.0 or later at www.wikiocean.net/license/ppl.html
* Software distributed under POOL Public License 1.0 is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
* The Initial Developer of the Original Code is Paritosh Pungaliya (C) 2008. All Rights Reserved.
******************************************************************************
* Objective of the additional terms (license)
* 1) Is to extend the software freedom to freedom to work.
* 2) To ensure that knowledge is free from monopoly of agencies.
* 3) To avoid a situation where big corporate or investor can buy out free software groups and companies and then start milking the communities built around it.
* (this trend can be seen how the freedom is curtailed in companies that get Venture Capital funding.)
******************************************************************************
*/

package  dbmanager;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Vector;
import utility.Input;



public class DBConnectionPool  implements Runnable
{	
	
	 private static Vector<Object> availableConnections, busyConnections;
	 private static int initialConnections,maxConnections;
	  
	static
	{
		 Input.initialize();
		 initialConnections = Input.INITIALCONNECTIONS;
		 maxConnections = Input.MAXCONNECTIONS;
		 if (initialConnections > maxConnections) 
		 {
			initialConnections = maxConnections;
		 }
		availableConnections = new Vector<Object>(initialConnections);
		busyConnections = new Vector<Object>();
		for(int i=0; i<initialConnections; i++) 
		{
			availableConnections.addElement(makeNewConnection());
		}
	 }
	
	
	// This explicitly makes a new connection. Called in
	  // the foreground when initializing the ConnectionPool,
	  // and called in the background when running.
	  
	 static public Connection makeNewConnection()  //throws SQLException	
	  {
		 try
	       {
	    	   Class.forName(Input.DB_DRIVER);
	    	   String dsn=Input.DB_PREFIX+"//"+Input.DB_HOST+"/"+Input.DB_NAME;
	    	   Connection con =DriverManager.getConnection(dsn,Input.DB_USERNAME,Input.DB_PASSWORD);
	    	   return con;
	       }catch(Exception e)
	       {
	    	   System.out.println("Error in getConnect() method");
	    	   e.printStackTrace();
	       }    
		    return null;
	  }
	 
	  
	  public static synchronized Connection getConnection() //throws SQLException
	  {
		  while(true)
		  {
		    if (!availableConnections.isEmpty())
		    {
		    	Connection existingConnection =  (Connection)availableConnections.lastElement();
			    int lastIndex = availableConnections.size() - 1;
			    availableConnections.removeElementAt(lastIndex);
			    
			    try 
			    {				
			    	 if (existingConnection.isClosed())
				      {
			    		  availableConnections.remove(existingConnection);
			    		  makeBackgroundConnection();
			    		  return(getConnection());
				      } 
				      else 
				      {
				        busyConnections.addElement(existingConnection);
				        return(existingConnection);
				      }	
				}
			    catch (Exception e) {
					 availableConnections.remove(existingConnection);
					 makeBackgroundConnection();
					 return(getConnection());
				}	
		    }
		    else 
		    {	      
		    	if ((totalConnections() < maxConnections) )
		    	{	        
		    		Connection db=makeNewConnection();
		    	 	availableConnections.add(db);
		    		return(getConnection());
		    	}	
		    }
		  }
	  }
	  
	  private static void makeBackgroundConnection() 
	  {		  
		    try 
		    {
		      Thread connectThread = new Thread();
		      connectThread.start();
		    } catch(OutOfMemoryError oome) {
		      // Give up on new connection
		    }
	 }
		 
	  public void run()
	  {
	    try 
	    {
	    	Connection db = makeNewConnection();
	      synchronized(this) {
	    	  availableConnections.addElement(db);
	        notifyAll();
	      }
	    } catch(Exception e) { // SQLException or OutOfMemory
	      // Give up on new connection and wait for existing one
	      // to free up.
	    }
	  }	  
	  
	  public static synchronized int totalConnections() 
	  {
		  //System.out.println(availableConnections.size() +""+  busyConnections.size());
		    return(availableConnections.size() +  busyConnections.size());
	  }
	  
	  public static synchronized void free(Connection dbObject)
	  {
		    busyConnections.removeElement(dbObject);
		    availableConnections.addElement(dbObject);
	  }    

	  /** Close all the connections. Use with caution:
	   *  be sure no connections are in use before
	   *  calling. Note that you are not <I>required</I> to
	   *  call this when done with a ConnectionPool, since
	   *  connections are guaranteed to be closed when
	   *  garbage collected. But this method gives more control
	   *  regarding when the connections are closed.
	   */
	 
	  public static synchronized void closeAllConnections() 
	  {
	    closeConnections(availableConnections);
	    availableConnections = new Vector<Object>();
	    closeConnections(busyConnections);
	    busyConnections = new Vector<Object>();
	  }
	 
	  public static void closeConnections(Vector<Object> connections)
	  {
		  try
		  {
		      for(int i=0; i<connections.size(); i++) 
		      {
		        Connection connection = (Connection)connections.elementAt(i);
		        if (!connection.isClosed())
		        {
		          connection.close();
		        }
		      }
		    } catch(SQLException sqle) {
		      // Ignore errors; garbage collect anyhow
		    }
	  }
	    
		  
	  public static void main(String[] args)
	  {		   
			 try 
			 {
				// ConnectionObjectPool conPool = new ConnectionObjectPool();			
				 Connection db=DBConnectionPool.getConnection();
				 System.out.println(db);
				 //free(db);
				 Connection db1=DBConnectionPool.getConnection();
				 System.out.println(db1);
				free(db);
				 Connection db2=DBConnectionPool.getConnection();
				 System.out.println(db2);
				 closeAllConnections();
				 Connection db3=DBConnectionPool.getConnection();
				 System.out.println(db3);
				 free(db1);
				 Connection db4=DBConnectionPool.getConnection();
				 System.out.println(db4);
				 free(db3);
				 
			} catch (Exception e) {
				// TODO: handle exception
			}		
	  }
}
